// Includes
#include <iostream>

#include "../utils/cuPrintf.cu"

//Incluyo la clase Random para la generación de números aleatorios en CUDA
#include "../utils/Random.cu"

//Incluyo la clase global.cu  que contiene las variables y funciones globales
#include "../utils/global.cu"

using namespace std;


/****************** Representacion real ********************/

/******************* Declaracion de Funciones del Host *************************/
void representacionReal();

/******************* Declaracion de Funciones Global del Device ********************/
__global__ void inicializarPoblacionReal(float* poblacion, float* fitnessActualDevice, struct Random randomGPU, int cantCromosomasPoblacion, int ejecutarFuncion);
__global__ void mainKernelReal(float* poblacionActualDevice, float* nuevaPoblacionDevice, float* fitnessActualDevice, float* nuevoFitnessDevice, float* individuosAMigrarDevice, struct Random r, int generacionActual);

/******************* Declaracion de Funciones internas del Device *****************/
__device__ int seleccionReal(float* vectorFitness, struct Random randomGPU, int ejecutarFuncion, int indiceMejor);
__device__ int seleccionPorTorneoReal(float* vectorFitness, struct Random randomGPU);
__device__ int seleccionElitistaReal(float* vectorFitness, int indiceMejor);

__device__ void cruzamientoRepresentacionReal(float* poblacionActual, float* nuevaPoblacion, int posicionCromosoma1, int posicionCromosoma2, int probabilidad, struct Random randomGPU, int ejecutarFuncion);
__device__ void cruzamientoAritmetico(float* poblacionActual, float* nuevaPoblacion, int posicionCromosoma1, int posicionCromosoma2, int probabilidad, struct Random randomGPU);
__device__ void cruzamientoAritmeticoIndividual(float* poblacionActual, float* nuevaPoblacion, int posicionCromosoma1, int posicionCromosoma2, int probabilidad, struct Random randomGPU);
__device__ void cruzamientoAritmeticoSimple(float* poblacionActual, float* nuevaPoblacion, int posicionCromosoma1, int posicionCromosoma2, int probabilidad, struct Random randomGPU);
__device__ void cruzamientoAritmeticoCompleto(float* poblacionActual, float* nuevaPoblacion, int posicionCromosoma1, int posicionCromosoma2, int probabilidad, struct Random randomGPU);

__device__ void mutacionReal(float* poblacion, int probabilidad, struct Random randomGPU, int ejecutarFuncion, int generacionActual);
__device__ void mutacionUniforme(float* poblacion, int probabilidad, struct Random randomGPU);
__device__ void mutacionNoUniforme(float* poblacion, int probabilidad, struct Random randomGPU, int generacionActual);

__device__ float calcularFitnessReal(float* poblacion, int posicion);

/******************* Implementacion de Funciones del Device *****************/


__global__ void inicializarPoblacionReal(float* poblacion, float* fitnessActualDevice, struct Random randomGPU, int cantCromosomasPoblacion, int ejecutarFuncion){
	//Obtengo posicion a escribir.
	int posicionCromosoma = threadIdx.y * cantGenesEnCromosomaDevice * 2 + blockIdx.y * totalGenesEnPoblacionDevice;
	int posicionFitness = threadIdx.y * 2 + blockIdx.y * cantCromosomasPoblacionDevice;
	for(int i = 0; i<cantGenesEnCromosomaDevice * 2; i++){
		//Genero valor aleatorio.
		float r = (getRandom(randomGPU, posicionCromosoma))/100;
		poblacion[posicionCromosoma+i] = r;
	}
	fitnessActualDevice[posicionFitness] = calcularFitnessReal(poblacion, posicionCromosoma);
	fitnessActualDevice[posicionFitness + 1] = calcularFitnessReal(poblacion, posicionCromosoma + cantGenesEnCromosomaDevice);

}


__global__ void mainKernelReal(float* poblacionActualDevice, float* nuevaPoblacionDevice, float* fitnessActualDevice, float* nuevoFitnessDevice, float* individuosAMigrarDevice, struct Random r, int generacionActual){
	//genero numero aleatorio entre 0 y cant individuos por poblacion
	int posicionCromosoma1 = seleccionReal(fitnessActualDevice, r, tipoSeleccion, 1);
	int posicionCromosoma2 = seleccionReal(fitnessActualDevice, r, tipoSeleccion, 2);
	cruzamientoRepresentacionReal(poblacionActualDevice, nuevaPoblacionDevice, posicionCromosoma1, posicionCromosoma2, probabilidadCruzamiento, r, tipoCruzamiento);

	mutacionReal(nuevaPoblacionDevice, probabilidadMutacion, r, tipoMutacion, generacionActual);
	//asignarFitness(nuevaPoblacionDevice, nuevoFitnessDevice);

	//__syncthreads();
	//if (threadIdx.x == 0) {
	///	individosAMigrarRepresentacionBinaria(nuevoFitnessDevice, nuevaPoblacionDevice, individuosAMigrarDevice);
	//}
}


/******************* Implementacion de Funciones de Migracion *****************/




/******************* Implementacion de Funciones de Selecccion *****************/

__device__ int seleccionReal(float* vectorFitness, struct Random randomGPU, int ejecutarFuncion, int indiceMejor){
	int resultado = -1;
	if(ejecutarFuncion == 0) {
		resultado = seleccionElitistaReal(vectorFitness, indiceMejor);
	}
	else if(ejecutarFuncion == 1) {
		resultado = seleccionPorTorneoReal(vectorFitness, randomGPU);
	}
	return resultado;
}


//Seleccion elitista
//selecciona el mejor o segundo mejor elemento segun se indica en indiceMejor con un 1 o con un 2
__device__ int seleccionElitistaReal(float* vectorFitness, int indiceMejor){
	cuPrintf("*** Seleccion elitista real ***\n");
	int mejor1 = -1;
	int mejor2 = -2;
	int indiceMejor1, indiceMejor2;
	float valorFitness;
	int indiceMemoria = blockIdx.y * cantCromosomasPoblacionDevice;
	for(int i = 0; i< cantCromosomasPoblacionDevice; i++){
		valorFitness = vectorFitness[indiceMemoria + i];
		if (valorFitness > mejor1) {
			mejor2 = mejor1;
			indiceMejor2 = indiceMejor1;
			mejor1 = valorFitness;
			indiceMejor1 = i;
		}
		else {
			if (valorFitness > mejor2){
				mejor2 = valorFitness;
				indiceMejor2 = i;
			}
		}
	}
	int posicionCromosomaMejor1 = blockIdx.y * cantCromosomasPoblacionDevice * cantGenesEnCromosomaDevice + indiceMejor1 * cantGenesEnCromosomaDevice;
	int posicionCromosomaMejor2 = blockIdx.y * cantCromosomasPoblacionDevice * cantGenesEnCromosomaDevice + indiceMejor2 * cantGenesEnCromosomaDevice;
	if (indiceMejor == 1) {
		return posicionCromosomaMejor1;
	}
	return posicionCromosomaMejor2;
}

//Seleccion por torneo
//selecciona 2 individuos al azar y devuelve la posicion del mejor
__device__ int seleccionPorTorneoReal(float* vectorFitness, struct Random randomGPU){
	cuPrintf("*** Seleccion por torneo real ***\n");
	unsigned int r = getRandomInt(randomGPU, threadIdx.y)% cantCromosomasPoblacionDevice;
	int posicionFitnessAleatoria1 = blockIdx.y * cantCromosomasPoblacionDevice + r;
	int posicionCromosomaAleatoria1 = blockIdx.y * cantCromosomasPoblacionDevice * cantGenesEnCromosomaDevice + r * cantGenesEnCromosomaDevice;
	r = getRandomInt(randomGPU, threadIdx.y + 1)% cantCromosomasPoblacionDevice;
	int posicionFitnessAleatoria2 = blockIdx.y * cantCromosomasPoblacionDevice + r;
	int posicionCromosomaAleatoria2 = blockIdx.y * cantCromosomasPoblacionDevice * cantGenesEnCromosomaDevice + r * cantGenesEnCromosomaDevice;
	if (vectorFitness[posicionFitnessAleatoria1] > vectorFitness[posicionFitnessAleatoria2])
		return posicionCromosomaAleatoria1;
	return posicionCromosomaAleatoria2;
}



/******************* Implementacion de Funciones de Cruzamiento *****************/

__device__ void cruzamientoRepresentacionReal(float* poblacionActual, float* nuevaPoblacion, int posicionCromosoma1, int posicionCromosoma2, int probabilidad, struct Random randomGPU, int ejecutarFuncion){

	if(ejecutarFuncion == 0) {
		cruzamientoAritmetico(poblacionActual, nuevaPoblacion, posicionCromosoma1, posicionCromosoma2, probabilidad, randomGPU);
	}
	else{
		if(ejecutarFuncion == 1){
			cruzamientoAritmeticoIndividual(poblacionActual, nuevaPoblacion, posicionCromosoma1, posicionCromosoma2, probabilidad, randomGPU);
		}
		else {
			if(ejecutarFuncion == 2) {
				cruzamientoAritmeticoSimple(poblacionActual, nuevaPoblacion, posicionCromosoma1, posicionCromosoma2, probabilidad, randomGPU);
			}
			else {
				if(ejecutarFuncion == 3) {
					cruzamientoAritmeticoCompleto(poblacionActual, nuevaPoblacion, posicionCromosoma1, posicionCromosoma2, probabilidad, randomGPU);
				}
			}
		}
	}
}


__device__ void cruzamientoAritmetico(float* poblacionActual, float* nuevaPoblacion, int posicionCromosoma1, int posicionCromosoma2, int probabilidad, struct Random randomGPU){
	cuPrintf("*** Cruzamiento Aritmetico ***\n");
	int posicionNuevoCromosoma1 = threadIdx.y * cantGenesEnCromosomaDevice * 2 + blockIdx.y * totalGenesEnPoblacionDevice;
	int posicionNuevoCromosoma2 = posicionNuevoCromosoma1 + cantGenesEnCromosomaDevice;

	unsigned int prob;
	float alfa = fmod(getRandom(randomGPU, posicionNuevoCromosoma1), 1);
	cuPrintf("alfa = %f", alfa);
	prob = (getRandomInt(randomGPU, posicionNuevoCromosoma1) % 101);
	if(prob < probabilidad ){
		for(int i=0; i<cantGenesEnCromosomaDevice; i++){
			nuevaPoblacion[posicionNuevoCromosoma1+i] = alfa * poblacionActual[posicionCromosoma1 + i] + (1-alfa) * poblacionActual[posicionCromosoma2 + i];
			nuevaPoblacion[posicionNuevoCromosoma2+i] = alfa * poblacionActual[posicionCromosoma2 + i] + (1-alfa) * poblacionActual[posicionCromosoma1 + i];
		}
	}else{
		for(int i=0; i<cantGenesEnCromosomaDevice; i++){
			nuevaPoblacion[posicionNuevoCromosoma1+i] = poblacionActual[posicionCromosoma1 + i];
			nuevaPoblacion[posicionNuevoCromosoma2+i] = poblacionActual[posicionCromosoma2 + i];
		}
	}
}

__device__ void cruzamientoAritmeticoIndividual(float* poblacionActual, float* nuevaPoblacion, int posicionCromosoma1, int posicionCromosoma2, int probabilidad, struct Random randomGPU){
	cuPrintf("*** Cruzamiento Aritmetico Individual ***\n");
	int posicionNuevoCromosoma1 = threadIdx.y * cantGenesEnCromosomaDevice * 2 + blockIdx.y * totalGenesEnPoblacionDevice;
	int posicionNuevoCromosoma2 = posicionNuevoCromosoma1 + cantGenesEnCromosomaDevice;

	unsigned int prob;
	int genAcruzar = (getRandomInt(randomGPU, posicionNuevoCromosoma1) % cantGenesEnCromosomaDevice);
	prob = (getRandomInt(randomGPU, posicionNuevoCromosoma1) % 101);
	for(int i=0; i<cantGenesEnCromosomaDevice; i++){
		nuevaPoblacion[posicionNuevoCromosoma1+i] = poblacionActual[posicionCromosoma1 + i];
		nuevaPoblacion[posicionNuevoCromosoma2+i] = poblacionActual[posicionCromosoma2 + i];
		if((i == genAcruzar) && (prob < probabilidad)){
			float alfa = fmod(getRandom(randomGPU, posicionNuevoCromosoma1 + i), 1);
			cuPrintf("alfa = %f", alfa);
			nuevaPoblacion[posicionNuevoCromosoma1+i] = alfa * poblacionActual[posicionCromosoma1 + i] + (1-alfa) * poblacionActual[posicionCromosoma2 + i];
			nuevaPoblacion[posicionNuevoCromosoma2+i] = alfa * poblacionActual[posicionCromosoma2 + i] + (1-alfa) * poblacionActual[posicionCromosoma1 + i];
		}
	}
}

__device__ void cruzamientoAritmeticoSimple(float* poblacionActual, float* nuevaPoblacion, int posicionCromosoma1, int posicionCromosoma2, int probabilidad, struct Random randomGPU){
	cuPrintf("*** Cruzamiento Aritmetico Simple ***\n");
	int posicionNuevoCromosoma1 = threadIdx.y * cantGenesEnCromosomaDevice * 2 + blockIdx.y * totalGenesEnPoblacionDevice;
	int posicionNuevoCromosoma2 = posicionNuevoCromosoma1 + cantGenesEnCromosomaDevice;

	unsigned int prob;
	unsigned int k = (getRandomInt(randomGPU, posicionNuevoCromosoma1) % cantGenesEnCromosomaDevice);
	cuPrintf("k = %d", k);
	float alfa = fmod(getRandom(randomGPU, posicionNuevoCromosoma1), 1);
	cuPrintf(" alfa = %f\n", alfa);
	for(int i=0; i<k; i++){
		nuevaPoblacion[posicionNuevoCromosoma1+i] = poblacionActual[posicionCromosoma1 + i];
		nuevaPoblacion[posicionNuevoCromosoma2+i] = poblacionActual[posicionCromosoma2 + i];
	}
	prob = (getRandomInt(randomGPU, posicionNuevoCromosoma1) % 101);
	if(prob < probabilidad ){
		for(int i=k; i<cantGenesEnCromosomaDevice; i++){
			nuevaPoblacion[posicionNuevoCromosoma1+i] = alfa * poblacionActual[posicionCromosoma2 + i] + (1-alfa) * poblacionActual[posicionCromosoma1 + i];
			nuevaPoblacion[posicionNuevoCromosoma2+i] = alfa * poblacionActual[posicionCromosoma1 + i] + (1-alfa) * poblacionActual[posicionCromosoma2 + i];
		}
	}else{
		for(int i=k; i<cantGenesEnCromosomaDevice; i++){
			nuevaPoblacion[posicionNuevoCromosoma1+i] = poblacionActual[posicionCromosoma1 + i];
			nuevaPoblacion[posicionNuevoCromosoma2+i] = poblacionActual[posicionCromosoma2 + i];
		}
	}
}

__device__ void cruzamientoAritmeticoCompleto(float* poblacionActual, float* nuevaPoblacion, int posicionCromosoma1, int posicionCromosoma2, int probabilidad, struct Random randomGPU){
	cuPrintf("*** Cruzamiento Aritmetico Completo ***\n");
	int posicionNuevoCromosoma1 = threadIdx.y * cantGenesEnCromosomaDevice * 2 + blockIdx.y * totalGenesEnPoblacionDevice;
	int posicionNuevoCromosoma2 = posicionNuevoCromosoma1 + cantGenesEnCromosomaDevice;

	unsigned int prob = (getRandomInt(randomGPU, posicionNuevoCromosoma1) % 101);
	float alfa = fmod(getRandom(randomGPU, posicionNuevoCromosoma1), 1);
	cuPrintf(" alfa = %f\n", alfa);
	if(prob < probabilidad ){
		for(int i=0; i<cantGenesEnCromosomaDevice; i++){
			nuevaPoblacion[posicionNuevoCromosoma1+i] = alfa * poblacionActual[posicionCromosoma1 + i] + (1-alfa) * poblacionActual[posicionCromosoma2 + i];
			nuevaPoblacion[posicionNuevoCromosoma2+i] = alfa * poblacionActual[posicionCromosoma2 + i] + (1-alfa) * poblacionActual[posicionCromosoma1 + i];
		}
	}else{
		for(int i=0; i<cantGenesEnCromosomaDevice; i++){
			nuevaPoblacion[posicionNuevoCromosoma1+i] = poblacionActual[posicionCromosoma1 + i];
			nuevaPoblacion[posicionNuevoCromosoma2+i] = poblacionActual[posicionCromosoma2 + i];
		}
	}
}


/******************* Implementacion de Funciones de Mutacion *****************/


__device__ void mutacionReal(float* poblacion, int probabilidad, struct Random randomGPU, int ejecutarFuncion, int generacionActual){

	if(ejecutarFuncion == 0) {
		mutacionUniforme(poblacion, probabilidad, randomGPU);
	}
	else{
		if(ejecutarFuncion == 1){
			mutacionNoUniforme(poblacion, probabilidad, randomGPU, generacionActual);
		}
	}
}


__device__ void mutacionUniforme(float* poblacion, int probabilidad, struct Random randomGPU){
	cuPrintf("*** Mutacion uniforme real ***\n");
	int posicionNuevoCromosoma1 = threadIdx.y * cantGenesEnCromosomaDevice * 2 + blockIdx.y * cantCromosomasPoblacionDevice * cantGenesEnCromosomaDevice;
	int posicionNuevoCromosoma2 = posicionNuevoCromosoma1 + cantGenesEnCromosomaDevice;

	unsigned int k;
	float left_k, right_k, aux1;
	float aux;
	unsigned int prob = (getRandomInt(randomGPU, posicionNuevoCromosoma1) % 101);
	if (prob < probabilidad ){
		k = getRandomInt(randomGPU, posicionNuevoCromosoma1 + 1) % cantGenesEnCromosomaDevice;
		//cuPrintf("k = %d \n",k);
		left_k = poblacion[posicionNuevoCromosoma1 + (k - 1) % cantGenesEnCromosomaDevice];
		right_k = poblacion[posicionNuevoCromosoma1 + (k + 1) % cantGenesEnCromosomaDevice];
		aux = getRandom(randomGPU, posicionNuevoCromosoma1);
		//cuPrintf("aux = %f \n",aux);
		if (left_k > right_k){
			aux1 = right_k;
			right_k = left_k;
			left_k = aux1;
		}
		poblacion[posicionNuevoCromosoma1 + k] = left_k + aux * (right_k - left_k);
	}

	prob = (getRandomInt(randomGPU, posicionNuevoCromosoma2) % 101);
	if (prob < probabilidad ){
		k = getRandomInt(randomGPU, posicionNuevoCromosoma2 + 1) % cantGenesEnCromosomaDevice;
		//cuPrintf("- individuo 2  -\n k = %d \n",k);
		left_k = poblacion[posicionNuevoCromosoma2 + (k - 1) % cantGenesEnCromosomaDevice];
		right_k = poblacion[posicionNuevoCromosoma2 + (k + 1) % cantGenesEnCromosomaDevice];
		aux = getRandom(randomGPU, posicionNuevoCromosoma2);
		//cuPrintf("aux = %f \n",aux);
		if (left_k > right_k){
			aux1 = right_k;
			right_k = left_k;
			left_k = aux1;
		}
		poblacion[posicionNuevoCromosoma2 + k] = left_k + aux * (right_k - left_k);
	}
}

__device__ void mutacionNoUniforme(float* poblacion, int probabilidad, struct Random randomGPU, int generacionActual){
	cuPrintf("*** Mutacion no uniforme real ***\n");
	int posicionNuevoCromosoma1 = threadIdx.y * cantGenesEnCromosomaDevice * 2 + blockIdx.y * cantCromosomasPoblacionDevice * cantGenesEnCromosomaDevice;
	int posicionNuevoCromosoma2 = posicionNuevoCromosoma1 + cantGenesEnCromosomaDevice;

	unsigned int k;
	float left_k, right_k, resultado, r;
	int aux;
	unsigned int prob = (getRandomInt(randomGPU, posicionNuevoCromosoma1) % 101);
	if (prob < probabilidad ){
		k = getRandomInt(randomGPU, posicionNuevoCromosoma1 + 1) % cantGenesEnCromosomaDevice;
		cuPrintf("k = %d \n",k);
		left_k = poblacion[posicionNuevoCromosoma1 + ((k - 1) % cantGenesEnCromosomaDevice)];
		right_k = poblacion[posicionNuevoCromosoma1 + ((k + 1) % cantGenesEnCromosomaDevice)];
		aux = getRandomInt(randomGPU, posicionNuevoCromosoma1) % 1;
		cuPrintf("aux = %f \n",aux);
		r = getRandom(randomGPU, posicionNuevoCromosoma1);
		if (aux == 0){
		/*	cuPrintf("generacionActual = %d \n", generacionActual);
			cuPrintf("numeroGeneracionesDevice = %d\n", numeroGeneracionesDevice);
			cuPrintf("r = %f\n", r);
			cuPrintf("pow = %f\n", __powf((1 - (generacionActual/numeroGeneracionesDevice)), gradoNoUniformidad));
		*/
			resultado = poblacion[posicionNuevoCromosoma1 + k] + ((right_k - poblacion[posicionNuevoCromosoma1 + k]) * r * (__powf((1 - (generacionActual/numeroGeneracionesDevice)), gradoNoUniformidad)));
		}
		else {
		/*	cuPrintf("generacionActual = %d \n", generacionActual);
			cuPrintf("numeroGeneracionesDevice = %d\n", numeroGeneracionesDevice);
			cuPrintf("r = %f\n", r);
			cuPrintf("pow = %f\n", __powf((1 - (generacionActual/numeroGeneracionesDevice)), gradoNoUniformidad));
		*/
			resultado = poblacion[posicionNuevoCromosoma1 + k] - ((poblacion[posicionNuevoCromosoma1 + k] - left_k) * r * (__powf((1 - (generacionActual/numeroGeneracionesDevice)), gradoNoUniformidad)));
		}
		poblacion[posicionNuevoCromosoma1 + k] = resultado;
	}

	prob = (getRandomInt(randomGPU, posicionNuevoCromosoma2) % 101);
	if (prob < probabilidad ){
		k = getRandomInt(randomGPU, posicionNuevoCromosoma2 + 1) % cantGenesEnCromosomaDevice;
		cuPrintf("k = %d \n",k);
		left_k = poblacion[posicionNuevoCromosoma2 + ((k - 1) % cantGenesEnCromosomaDevice)];
		right_k = poblacion[posicionNuevoCromosoma2 + ((k + 1) % cantGenesEnCromosomaDevice)];
		aux = getRandomInt(randomGPU, posicionNuevoCromosoma2) % 1;
		cuPrintf("aux = %f \n",aux);
		r = getRandom(randomGPU, posicionNuevoCromosoma2);
		if (aux == 0){
			resultado = poblacion[posicionNuevoCromosoma2 + k] + ((right_k - poblacion[posicionNuevoCromosoma2 + k]) * r * (__powf((1 - (generacionActual/numeroGeneracionesDevice)), gradoNoUniformidad)));
		}
		else {
			resultado = poblacion[posicionNuevoCromosoma2 + k] - ((poblacion[posicionNuevoCromosoma2 + k] - left_k) * r * (__powf((1 - (generacionActual/numeroGeneracionesDevice)), gradoNoUniformidad)));
		}
		poblacion[posicionNuevoCromosoma2 + k] = resultado;
	}
}

/******************* Implementacion de Funciones de fitness *****************/

__device__ float calcularFitnessReal(float* poblacion, int posicion) {
	float fitness = 0;
	float contador = cantGenesEnCromosomaDevice-1;
	for(int i=0; i<cantGenesEnCromosomaDevice; i++){
	  fitness = fitness + poblacion[posicion + i];
	  contador--;
	}
	return fitness;
}

/******************* Implementacion de Funciones del host *****************/


void representacionReal() {
	int tamVectorPoblacion = sizeof(float)*totalGenes;
	int tamVectorFitness = sizeof(float)*totalIndividuos;
	int tamVectorIndividuosAMigrar = sizeof(float)*cantGenesEnCromosoma*cantidadPoblaciones;

	static float *poblacionActualHost = (float *) malloc(tamVectorPoblacion);
	static float *nuevaPoblacionHost = (float *) malloc(tamVectorPoblacion);
	static float *fitnessActualHost = (float *) malloc(tamVectorFitness);
	static float *nuevoFitnessHost = (float *) malloc(tamVectorFitness);
	static float *individuosAMigrarHost = (float *) malloc(tamVectorIndividuosAMigrar);

	float *poblacionActualDevice;
	float *nuevaPoblacionDevice;
	float *fitnessActualDevice;
	float *nuevoFitnessDevice;
	float *individuosAMigrarDevice;

	// alocar en el device
	cudaMalloc(&poblacionActualDevice, tamVectorPoblacion);
	cudaMalloc(&nuevaPoblacionDevice, tamVectorPoblacion);
	cudaMalloc(&fitnessActualDevice, tamVectorFitness);
	cudaMalloc(&nuevoFitnessDevice, tamVectorFitness);
	cudaMalloc(&individuosAMigrarDevice, tamVectorIndividuosAMigrar);

	//Semilla randomica.
	//srand48(SEMILLA);

	//Inicio numeros aleatorios.
	struct Random r = initRandom(cantCromosomasPoblacion/2);

	//cuantos bloques? (1)
	dim3 tamGrid(1, cantidadPoblaciones); //Grid dimensión
	//cuantos hilos por bloque? (N) o sea tengo un bloque y n hilos (un hilo por columna)
	dim3 tamBlock(1,cantCromosomasPoblacion/2,1); //Block dimensión

	dim3 tamBlockMigracion(1,1,1); //Block dimensión

	//inicializacion de la poblacion en GPU
	inicializarPoblacionReal<<<tamGrid, tamBlock>>>(poblacionActualDevice, fitnessActualDevice, r, cantCromosomasPoblacion, 2);

	// Traer resultado
	cudaMemcpy (fitnessActualHost, fitnessActualDevice, tamVectorFitness, cudaMemcpyDeviceToHost);
	cudaMemcpy (poblacionActualHost, poblacionActualDevice, tamVectorPoblacion, cudaMemcpyDeviceToHost);

	cout << "tot " << totalGenes << " POBLACION INICIAL\n";
	cout << totalGenes;
	for (int i = 0; i<totalGenes; i++){
		if(i%cantGenesEnCromosoma == 0){
			cout << "\n";

		}
		printf("%f ", poblacionActualHost[i]);
	}
	cout << "\n\n\nFITNESS\n";

	for (int i = 0; i<cantCromosomasPoblacion * cantidadPoblaciones; i++){
		printf("%f ", fitnessActualHost[i]);
	}
	cout << "\n\n\n\n";

	//int generacionesMigrar = 1;
	int iteracion = 0;
	//los individuos de la poblacion actual se van intercambiando en el reemplazo
	while(iteracion < numeroGeneraciones){
		//si la iteracion es par invoco a GPU con poblacionActual, nuevaPoblacion
		if ((iteracion % 2) == 0) {
			// seleccionamos, cruzamos y mutamos en GPU
			mainKernelReal<<<tamGrid, tamBlock>>>(poblacionActualDevice, nuevaPoblacionDevice, fitnessActualDevice, nuevoFitnessDevice, individuosAMigrarDevice, r, iteracion);

			//Si hay que migrar
			//if (iteracion % generacionesMigrar == 0) {
			//	migracionRepresentacionBinaria<<<tamGrid, tamBlockMigracion>>>(nuevaPoblacionDevice, nuevoFitnessDevice, individuosAMigrarDevice, r, iteracion);
			//}
		}
		//sino invoco a GPU con nuevaPoblacion, poblacionActual
		else {

			// seleccionamos, cruzamos y mutamos en GPU
			mainKernelReal<<<tamGrid, tamBlock>>>(nuevaPoblacionDevice, poblacionActualDevice, nuevoFitnessDevice, fitnessActualDevice, individuosAMigrarDevice, r, iteracion);

			//Si hay que migrar
			//if (iteracion % generacionesMigrar == 0) {
			//	migracionRepresentacionBinaria<<<tamGrid, tamBlockMigracion>>>(poblacionActualDevice, fitnessActualDevice, individuosAMigrarDevice, r);
			//}
		}

		//mejorFitness = obtenerMejorFitness(poblacionActual,mejorCromosoma);
		iteracion++;
	}

	// Traer resultado
	cudaMemcpy (nuevoFitnessHost, nuevoFitnessDevice, tamVectorFitness, cudaMemcpyDeviceToHost);
	cudaMemcpy (nuevaPoblacionHost, nuevaPoblacionDevice, tamVectorPoblacion, cudaMemcpyDeviceToHost);
	cudaMemcpy (individuosAMigrarHost, individuosAMigrarDevice, tamVectorIndividuosAMigrar, cudaMemcpyDeviceToHost);

	// liberar memoria de las matrices en el device
	cudaFree(fitnessActualDevice);
	cudaFree(poblacionActualDevice);
	cudaFree(nuevoFitnessHost);
	cudaFree(nuevaPoblacionDevice);

	cout << "NUEVA POBLACION\n";
	for (int i = 0; i<totalGenes; i++){
		if(i%cantGenesEnCromosoma == 0){
			cout << "\n";

		}
		printf("%f ", nuevaPoblacionHost[i]);
	}
	cout << "\n\n\nFITNESS\n";

	for (int i = 0; i<cantCromosomasPoblacion * cantidadPoblaciones; i++){
		printf("%f ", nuevoFitnessHost[i]);
	}
	cout << "\n";

	/*cout << "\n\nA MIGRAR\n";

	for (int i = 0; i<cantGenesEnCromosoma * cantidadPoblaciones; i++){
		if(i%cantGenesEnCromosoma == 0){
			cout << "\n";
		}
		printf("%i ", individuosAMigrarHost[i]);
	}
	cout << "\n";
	*/
}

